package com.vhall.component.framework.advice;

import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.vhall.component.framework.common.exception.ExceptionResult;
import com.vhall.component.framework.common.model.Result;
import com.vhall.component.framework.common.support.SecretKeyThreadLocal;
import com.vhall.component.framework.common.support.UserTicketVO;
import com.vhall.component.framework.common.utils.AesUtil;
import com.vhall.component.framework.common.utils.RequestHeadersUtil;
import com.vhall.component.framework.common.utils.WebUtils;
import com.vhall.component.framework.config.BaseWebMvcConfig;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter;
import org.springframework.core.io.Resource;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Objects;

/**
 * response handler advice
 *
 * @author xiaoliang.wang
 */
@Slf4j
@ControllerAdvice

public class ResponseHandlerAdvice implements ResponseBodyAdvice {

    @Autowired
    private ObjectMapper objectMapper;

    public static final String CALLBACK_URL = "/callback/index" ;

    public static final String CALLBACK_LIVE_URL = "/callback/lives/index" ;

    public static final String CALLBACK_RECORD_URL = "/callback/record/index" ;

    public static final String CALLBACK_DOCUMENT_URL = "/callback/documents/index" ;
    /**
     * 不做返回结果包装的 url
     */
    private static final HashSet<String> NOT_HANDLE_URLS = new HashSet<>();
    static{
        NOT_HANDLE_URLS.add(CALLBACK_URL);
        NOT_HANDLE_URLS.add(CALLBACK_LIVE_URL);
        NOT_HANDLE_URLS.add(CALLBACK_RECORD_URL);
        NOT_HANDLE_URLS.add(CALLBACK_DOCUMENT_URL);
        NOT_HANDLE_URLS.add("/v4/test/xtgzstg/sso/check");
    }

    @Override
    @SuppressWarnings("NullableProblems")
    public boolean supports(MethodParameter returnType, Class converterType) {
        Method method = returnType.getMethod();
        if (Objects.nonNull(method)) {
            return !method.getReturnType().isAssignableFrom(Void.TYPE) && converterType
                    .isAssignableFrom(BaseWebMvcConfig.StringOrMappingJackson2HttpMessageConverter.class);
        }
        return false;
    }

    @SneakyThrows
    @Override
    @SuppressWarnings("NullableProblems")
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType,
                                  ServerHttpRequest request, ServerHttpResponse response) {

        // 别的系统直接调用的函数直接返回
        String path = request.getURI().getPath();
        if (StrUtil.isNotBlank(path) && StrUtil.contains(path,"//")) {
            path = path.replace("//","/");
        }
        if (NOT_HANDLE_URLS.contains(path)) {
            return body;
        }

        // 文件上传下载要做处理，不能封装，直接返回
        if (body instanceof Resource) {
            return body;
        }
        // error
        if (body instanceof ExceptionResult) {
            ExceptionResult errorInfo = (ExceptionResult) body;
            WebUtils.setResult(((ServletServerHttpRequest) request).getServletRequest(), errorInfo);
            return errorInfo;
        }

        // feign request
        if (RequestHeadersUtil.isFeignRequest(((ServletServerHttpRequest) request).getServletRequest())) {
            // 记录 Controller 结果
            WebUtils.setResult(((ServletServerHttpRequest) request).getServletRequest(), body);
            return body;
        }

        // success 注:这里必须要用ObjectMapper，否则日期格式化会不生效
        Result result = Result.success(objectMapper.valueToTree(body));
        UserTicketVO ticket = SecretKeyThreadLocal.getTicket();
        if (Objects.nonNull(result.getData()) && Objects.nonNull(ticket)) {
            result.setData(AesUtil.encryptPKCS7(result.getData().toString(), ticket.getSecretKey(), ticket.getSecretIv()));
        }
        WebUtils.setResult(((ServletServerHttpRequest) request).getServletRequest(), result);
        return result;
    }

}